using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Net;
using System.Net.Sockets;
using System.Net.NetworkInformation;

namespace AGameOfThrones.network
{
    public class server
    {
        private Thread m_ListenerThread;
        private List<connection> m_Connections;
        private bool m_Running;

        public game.game Game
        {  
            get
            {
                try
                {
                    return System.Windows.Application.Current.FindResource("gamestate") as game.game;
                }
                catch (System.Exception ex)
                {
                    Console.WriteLine(ex);
                    return null;
                }

            }
        }        


        public server()
		{
            m_ListenerThread = new Thread(new ThreadStart(threadmain));
            m_ListenerThread.IsBackground = true;
            m_Connections = new List<connection>();
            m_Running = false;
		}
        
        public void Start()
        {
            m_Running = true;
            m_ListenerThread.Start();
        }

        public void Abort()
        {
            m_Running = false;
            m_ListenerThread.Abort();
            m_ListenerThread.Join();
        }

        public void Shutdown()
        {
            m_Running = false;
            Thread.Sleep(100);
            Abort();
            lock (m_Connections)
            {
                foreach (connection connection in m_Connections)
                {
                    connection.Abort();
                }
            }
        }

        public void BroadcastMessage(messages.message message)
        {
            lock (m_Connections)
            {
                foreach (connection connection in m_Connections)
                {
                    connection.SendMessage(message);
                }
            }
        }

        public void BroadcastData(byte[] buffer, int bufflen)
        {
            lock (m_Connections)
            {
                foreach (connection connection in m_Connections)
                {
                    connection.SendData(buffer, bufflen);
                }
            }
        }

        public void SpewLog(String in_Text)
        {
            Console.WriteLine("Network Spew " + in_Text);
        }

        public void ErrorLog(String in_Text)
        {
            Console.WriteLine("Network Error " + in_Text);
        }

        public int Port { get { return 48927; } }

        private void threadmain()
        {
            try
            {
                TcpListener listener = new TcpListener(IPAddress.Any, Port);
                //TcpListener listener = new TcpListener(IPAddress.IPv6Any, Port);
                //listener.AllowNatTraversal(true); 
                listener.Start();


                var addressInfoCollection =
                           IPGlobalProperties.GetIPGlobalProperties()
                             .GetUnicastAddresses();

                foreach (var addressInfo in addressInfoCollection)
                {
                    Console.WriteLine("Address: {0}",
                         addressInfo.Address);
                } 

                while (m_Running)
                {
                    try
                    {
                        if (listener.Pending())
                        {
                            TcpClient clientSocket = listener.AcceptTcpClient();
                            string connectingClientAddress = (clientSocket.Client.RemoteEndPoint as IPEndPoint).Address.ToString();
    
                            // see if this is the same connection reconnecting
                            lock (m_Connections)
                            {
                                foreach (connection c in m_Connections)
                                {
                                    if ((c.TcpClient.Client.RemoteEndPoint as IPEndPoint).Address.ToString() == connectingClientAddress)
                                    {
                                        SpewLog("Reconnected connection");
                                        c.Abort();  
                                        break;
                                    }
                                }
                            }

                            SpewLog("Client connected!");
                            connection conn = new connection(clientSocket);
                            conn.OnNetworkConnected += new connection.OnNetworkConnectedDelegate(conn_OnNetworkConnected);
                            conn.OnNetworkDisConnected += new connection.OnNetworkDisConnectedDelegate(conn_OnNetworkDisConnected);
                            conn.OnNetworkData += new connection.OnNetworkDataDelegate(conn_OnNetworkData);
                            conn.Start();
                        }
                        else
                        {
                            Thread.Sleep(25);
                        }
                    }
                    catch (Exception ex)
                    {
                        ErrorLog(ex.ToString());
                        Thread.Sleep(100);
                    }
                }

                listener.Stop();
            }
            catch (ThreadAbortException abortException)
            {
                ErrorLog((string)abortException.ExceptionState);
            }
            catch (Exception)
            {
            }

        }

        void conn_OnNetworkData(object sender, connection.OnNetworkDataEvent e)
        {
            if (e.Message.GetType() == typeof(messages.request_movepiece))
            {
                if (Game != null)
                {
                    messages.request_movepiece r = e.Message as messages.request_movepiece;
                    if (Game.MovePiece(r.PieceName, r.NewPosition))
                    {
                        messages.process_movepiece p = new messages.process_movepiece();
                        p.PieceName = r.PieceName;
                        p.OldPosition = r.OldPosition;
                        p.NewPosition = r.NewPosition;
                        BroadcastMessage(p);
                    }
                    Console.WriteLine("Got request movepiece message");
                }
            }
        }

        void conn_OnNetworkConnected(object sender, connection.OnNetworkConnectedEvent e)
        {
            AddClient(sender as connection);
        }

        void conn_OnNetworkDisConnected(object sender, connection.OnNetworkDisConnectedEvent e)
        {
            RemoveClient(sender as connection);
        }

        public void RemoveClient(connection connection)
        {
            lock (m_Connections)
            {
                m_Connections.Remove(connection);
            }
            if (OnClientDisconnect != null)
            {
                OnClientDisconnect();
            }
        }

        public void AddClient(connection connection)
        {
            lock (m_Connections)
            {
                m_Connections.Add(connection);
            }
            if (OnClientConnect != null)
            {
                OnClientConnect();
            }
        }

        public delegate void OnClientConnectDelegate();
        public static event OnClientConnectDelegate OnClientConnect;

        public delegate void OnClientDisconnectDelegate();
        public static event OnClientDisconnectDelegate OnClientDisconnect;


    }
}
